home *** CD-ROM | disk | FTP | other *** search
- /* CROND: (c) Kees Lemmens, Netherlands; June 1993.
-
- Program for ATARI ST (running under MINT) to make it possible
- to run background jobs at regular intervals.
-
- This is the daemon itself. It forks automatically when started
- and then tests every minute if one of the jobs in the joblist
- should be run.
- The joblist is only builded when the daemon starts and when it
- receives a signal from another process. (i.e. CRONTAB or AT)
-
- A pipe is used to supply commands to the daemon. This pipe also
- functions as a lock and thus prevents a second daemon to be
- started by accident.
-
- I choose to use a bitmask on the job schedule times, and I can
- tell you it took me a hell lot of time to get a good idea about
- how to do it !
-
- Version 2.1 :
-
- - Now cron also takes care of the right user and group id's for
- the spawned processes.
- Also AT and CRONTAB use the Pgetuid() function after the
- environment variable LOGNAME to determine the username.
- However the username can also be supplied with the -u option or
- by redefinition of LOGNAME : So you see this isn't very secure !
-
- But after all, MiNT is not supposed to be a secure system.
-
- - Before the job starts the working directory is now changed to
- the one specified in the passwd file.
-
- - I replaced the old SIGHUP mechamism with a COMMANDPIPE, because
- users with non-zero uid's aren't allowed to give signals to the
- crondaemon and I didn't see another way to change this.
-
- Any questions or suggestions about this program can be send to:
- lemmens@dv.twi.tudelft.nl
- */
-
- #include "cron.h"
-
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <time.h>
- #include <ctype.h>
- #include <sys\wait.h>
- #include <errno.h>
- #include <signal.h>
-
- /* __MINT__ must be predefined or else we have wrong signals ! */
-
- static int cronpipe;
-
- extern void exit(int);
- extern void cronsleep(unsigned long);
- extern char *ux2dos(char *string);
-
- extern void DoJobs(entry joblist[],int jobcount,active ajoblist[]);
- extern void HandleExits(active actjoblist[]);
- extern void LogMsg(char *name, int pid, char *fmt,...);
- extern void PokeDaemon(int cmd);
-
- /* CreatePipe() - write our PID into /etc/crond.pid and create
- the command pipe (unless another daemon is already running).
- */
-
- void CreatePipe(void)
- { char spid[10];
- int pidfile;
-
- if ((cronpipe = open(ux2dos(CRONPIPE),O_RDWR|O_CREAT)) < 0)
- { fprintf(stderr,PROGNAME": Can't create command pipe !\n");
- exit(1);
- }
-
- if ((pidfile = open(ux2dos(PIDFILE),O_WRONLY|O_CREAT)) < 0)
- { fprintf(stderr,PROGNAME": Can't open %s\n",PIDFILE);
- exit(1);
- }
- sprintf(spid, "%.4d\n", getpid());
- write(pidfile,spid,strlen(spid));
- close(pidfile);
- }
-
- void DaemonMode(void)
- {
- #ifndef DEBUG
- switch ((int)fork())
- { case -1:
- fprintf(stderr,"%s: can't fork daemon !\n",PROGNAME);
- exit(1);
- break;
- case 0:
- /* start child process */
- (void)setpgrp(0,0);
- (void)kill(getppid(),SIGKILL); /* kill parent */
- (void)nice(NICELEVEL); /* don't disturb others */
- LogMsg(PROGNAME,getpid(),"Cron daemon started");
- break;
- default:
- /* parent process should wait to be killed */
- wait();
- break;
- }
- #endif
- }
-
- int CalcStartTimes(entry joblist[],int jobcount,time_t starttm)
- { int x,runjobs=0;
- struct tm *ti;
- entry msk;
-
- ti = localtime(&starttm);
-
- msk.MinH = (ti->tm_min < 32) ? 0L:1L<<(ti->tm_min - 32);
- msk.MinL = (ti->tm_min >= 32) ? 0L:1L<<ti->tm_min;
-
- msk.Hour = 1L<<ti->tm_hour;
- msk.DayOfM = 1L<<ti->tm_mday;
- msk.Month = 1<<(ti->tm_mon+1); /* must be from 1-12 */
- msk.DayOfW = 1<<ti->tm_wday;
-
- #ifdef DEBUG
- Debug("Current time mask :");
- DebugPrintTimes(&msk);
- #endif
-
- for(x=0;x<jobcount;x++)
- { if
- ( ((msk.MinH & joblist[x].MinH ) /* must be <OR> ! */
- || (msk.MinL & joblist[x].MinL ))
- && (msk.Hour & joblist[x].Hour )
- && (msk.DayOfM & joblist[x].DayOfM)
- && (msk.Month & joblist[x].Month )
- && (msk.DayOfW & joblist[x].DayOfW)
- )
- { joblist[x].Status = START;
- runjobs++;
- #ifdef DEBUG
- Debug("CalcStartTimes: job %d will start next run\n",x);
- #endif
- }
- else
- joblist[x].Status = SLEEP;
- }
- return runjobs;
- }
-
- /* next routine is both used for minutes ( 2 seperate fields, because
- 32 bits isn't enough ) as for hours, day of month, month and day of
- week that can do with one field.
-
- This implies we always have to supply two variables : a low part
- and a high part. The high part is only set if the number of bits
- is greater than 32, so only for the minutes we have to supply a
- valid pointer for the high part.
-
- This saves the need of two separate functions and doesn't make the
- code much more complicated. (to my opinion !)
- */
-
- int FillTimeMask(ULONG *maskH,ULONG *maskL,char *string,int nrbits)
- { long tmpmaskH = 0, tmpmaskL = 0L;
- char *tmp;
- int start,end,shift;
-
- *maskL = 0L;
- if(nrbits > 32) *maskH = 0L;
-
- /* test for * which means set all bits */
-
- if (*string == '*')
- { *maskL = -1L;
- if(nrbits > 32) *maskH = -1L;
- return 0;
- }
-
- /* search for intervals ( - ) */
-
- if(strchr(string,'-') != NULL)
- { sscanf(string,"%d-%d",&start,&end);
- if(start > end)
- { LogMsg(PROGNAME,getpid(),"Illegal interval %d-%d: ignored",
- start,end);
- return -1;
- }
- if(start > nrbits || end > nrbits)
- { LogMsg(PROGNAME,getpid(),"Interval overflow %d-%d: corrected",
- start,end);
- if (end > nrbits)
- end = nrbits;
- if(start > nrbits)
- return -1;
- }
- if(end >= 32 && nrbits > 32)
- { shift = start < 32 ? 0 : start-32;
- tmpmaskH = (1L<<(end - 32 + 1 - shift )) - 1L;
- *maskH = tmpmaskH<<shift;
- }
- if(start < 32)
- { tmpmaskL = (1L<<( (end > 32 ? 32 : end) + 1 - start)) - 1L;
- *maskL = tmpmaskL<<start;
- }
-
- return 0;
- }
-
- /* do single fields and also search for comma's (,) */
-
- tmp = string;
- do
- { if(*tmp == ',')
- string = tmp + 1;
-
- start = atoi(string);
- if(start > nrbits)
- { LogMsg(PROGNAME,getpid(),"Field overflow %d: ignored",
- start);
- continue;
- }
- if(start < 32)
- *maskL |= 1L<<start;
- else if(nrbits > 32)
- *maskH |= 1L<<(start - 32);
- }while((tmp=strchr(string,',')) != NULL);
-
- return 0;
- }
-
- int ReadEntry(FILE *cronfile, entry *line)
- { char *tmp,*ptr,buf[MAX_TEMPSTR];
- char *number[5];
- ULONG tmpfield;
- int x;
-
- ptr=fgets(buf,MAX_TEMPSTR - 1, cronfile);
-
- if(feof(cronfile)) return -1;
-
- for(x=0;x<5;x++)
- { while (*ptr=='\t' || *ptr==' ') ptr++;
- number[x] = ptr;
- while (*ptr!='\t' && *ptr!=' ' &&
- *ptr!='\n' && *ptr!='\0') ptr++;
- *ptr++ ='\0';
- }
- while (*ptr=='\t' || *ptr==' ') ptr++;
-
- if((tmp=strchr(ptr,'\n')) != NULL) *tmp = '\0';
- strncpy(line->Command,ptr,MAX_COMMAND - 1);
-
- FillTimeMask(&line->MinH ,&line->MinL ,number[0],MAX_MIN);
- FillTimeMask((ULONG *)NULL,&line->Hour ,number[1],MAX_HOUR);
- FillTimeMask((ULONG *)NULL,&line->DayOfM,number[2],MAX_DAYOFM);
-
- FillTimeMask((ULONG *)NULL,&tmpfield ,number[3],MAX_MONTH);
- line->Month = (UINT)tmpfield;
-
- FillTimeMask((ULONG *)NULL,&tmpfield ,number[4],MAX_DAYOFW);
- line->DayOfW = (UCHAR)tmpfield;
-
- return 0;
- }
-
- int RebuildDir(char *dirname,char jobtype,int jobcount,entry joblist[])
- { char cronname[MAX_FNAME];
- long DirHandle;
- struct dirent d_ent;
- char *fileid;
- FILE *cronfile;
-
- if((DirHandle=opendir(ux2dos(dirname))) < 0)
- { fprintf(stderr,"Directory %s doesn't exist !\n",dirname);
- exit(1);
- }
-
- /* Rem. : Dreaddir is MINT specific call */
-
- while(Dreaddir(sizeof(d_ent),DirHandle,&d_ent.d_ino) == 0L)
- { if (*d_ent.d_name == '.')
- continue; /* skip . and .. */
-
- sprintf(cronname,"%s/%s",dirname,d_ent.d_name);
-
- if((cronfile = fopen(ux2dos(cronname),"r")) == NULL)
- { LogMsg(PROGNAME, getpid(), "Read failed for %s",cronname);
- continue;
- }
-
- while(jobcount < MAX_JOBS)
- { int x;
-
- if(ReadEntry(cronfile,&joblist[jobcount]) < 0)
- break;
-
- if((fileid = strchr(d_ent.d_name,'.')) != NULL)
- { *fileid++ = '\0'; /* separate name and extension */
- joblist[jobcount].AtId = atoi(fileid);
- }
-
- for(x=0;x<MAX_UNAME -1 && d_ent.d_name[x];x++)
- joblist[jobcount].User[x] = tolower(d_ent.d_name[x]);
-
- joblist[jobcount].User[x] = '\0';
-
- /* User must be lowercase to use pwgetnam() function */
-
- joblist[jobcount].Type = jobtype;
-
- #ifdef DEBUG
- Debug("RebuildTables: Command %d =\"%s\" for %s added\n",
- jobcount,joblist[jobcount].Command,
- joblist[jobcount].User);
-
- if(jobtype == AT)
- Debug("AT Job id : %03d\n",joblist[jobcount].AtId);
-
- Debug("Job %d (type %d) will run at :",jobcount,jobtype);
- DebugPrintTimes(&joblist[jobcount]);
- #endif
- jobcount++;
- }
- if(jobcount >= MAX_JOBS)
- LogMsg(PROGNAME,getpid(),"No more jobs, table full !");
-
- fclose(cronfile);
- }
- closedir(DirHandle);
-
- return jobcount;
- }
-
- int RebuildTables(entry joblist[])
- { int jobcount;
-
- jobcount = RebuildDir(CRONDIR,CRON,0,joblist);
- jobcount = RebuildDir(ATDIR,AT,jobcount,joblist);
-
- return jobcount;
- }
-
- void SigIntHandler(void)
- { close(cronpipe);
- LogMsg(PROGNAME,getpid(),"Terminate signal received");
- remove(PIDFILE); /* remove pidfile */
- exit(0);
- }
-
- void main(void)
- { time_t currenttime,starttime;
- char cmd;
- long interval;
- int jobcount,runjobs;
- static entry joblist[MAX_JOBS];
- static active ajoblist[MAX_SIMJOBS];
-
- /* Fixed number of jobs and active jobs to avoid system overload */
- /* In active joblist pids from running jobs are saved until finished */
-
- if(getuid() != 0)
- { fprintf(stderr,"Must have uid 0 !\n");
- exit(1);
- }
- DaemonMode();
- CreatePipe();
-
- #ifndef DEBUG
- signal(SIGINT,SigIntHandler);
- signal(SIGTERM,SigIntHandler);
- signal(SIGHUP,SIG_IGN);
- signal(SIGCHLD,SIG_IGN);
-
- sigblock((1L<<SIGINT) | (1L<<SIGTERM));
- /* block SIGINT and SIGTERM : only handled in sleep */
- #endif
-
- PokeDaemon(INTERNBUILD);
-
- while (TRUE)
- {
- #ifndef DEBUG
- starttime = ((time(¤ttime) / 60L) +1) * 60L;
- #else
- starttime = currenttime; /* faster testing ! */
- Debug("Next run at %.24s with %d jobs\n",ctime(&starttime),activjobs);
- #endif
- do
- {
- if(Finstat(cronpipe) > 0 ) /* check for commands */
- { read(cronpipe,&cmd,1);
- switch(cmd)
- { case EXTERNBUILD: /* log only external cmds */
- LogMsg(PROGNAME,getpid(),"REREAD : crontabs rebuilded");
- case INTERNBUILD:
- jobcount = RebuildTables(joblist);
- break;
- default:
- LogMsg(PROGNAME,getpid(),"Unknown command on pipe");
- }
- }
-
- if(jobcount > 0)
- runjobs=CalcStartTimes(joblist,jobcount,starttime);
- #ifdef DEBUG
- else
- Debug("Currently no jobs in table\n");
- #endif
- interval = starttime - currenttime;
- cronsleep(interval < SLEEPTIME ? interval : SLEEPTIME);
- }
- while((starttime - time(¤ttime)) > 1L);
-
- HandleExits(ajoblist); /* from previous run(s) */
- if(runjobs > 0)
- DoJobs(joblist,jobcount,ajoblist);
- }
- }